package heap.heapSort;

/**
 * 构造函数传入数组，逐渐进行heapify操作，使其变成标准堆
 * mxf
 * 2018年5月29日08:44:01
 */
public class MaxHeap<Item extends Comparable> {
    protected Item[] data; // 数组
    protected int count; // 元素的个数
    protected int capacity; // 数组的长度

    // 构造函数, 构造一个空堆, 可容纳capacity个元素
    public MaxHeap(int capacity){
        this.data = (Item[])new Comparable[capacity+1];
        this.count = 0;
        this.capacity = capacity;
    }

    /**
     * 传入数组，n为元素的个数，进行heapify操作
     * mxf
     * 2018年5月29日08:46:43
     * @param arr
     */
    public MaxHeap(Item[] arr) {
        int n = arr.length;
        this.capacity = n;
        this.data = (Item[])new Comparable[n+1];
        for(int i = 0; i < n; i++) {
            data[i + 1] = arr[i];
        }
        this.count = n;
        for(int i = count / 2; i >= 1; i--) {
            shiftDown(i);
        }
    }

    // 返回堆中的元素个数
    public int size(){
        return count;
    }

    // 返回一个布尔值, 表示堆中是否为空
    public boolean isEmpty(){
        return count == 0;
    }

    public void insert(Item item) {
        assert count + 1 <= capacity;
        count++;
        data[count] = item;
        shiftUp(count);
    }

    //********************
    //* 最大堆核心辅助函数
    //********************
    private void shiftUp(int k) {
        while (k > 1 && data[k].compareTo(data[k / 2]) > 0) {
            swap(k, k / 2);
            k = k / 2;
        }
    }


    public Item extractMax() {
        assert count > 0;
        Item max = data[1];
        data[1] = data[count];
        count--;
        shiftDown(1);
        return max;
    }

    private void shiftDown(int k) {
        while(2 * k <= count) {
            int j = 2 * k;
            if(j + 1 <= count && data[j].compareTo(data[j + 1]) < 0) {
                j++;
            }
            if(data[k].compareTo(data[j]) > 0) {
                break;
            }
            swap(k, j);
            k = j;
        }
    }

    // 交换堆中索引为i和j的两个元素
    private void swap(int i, int j){
        Item t = data[i];
        data[i] = data[j];
        data[j] = t;
    }

    // 测试 MaxHeap
    public static void main(String[] args) {

        heap.shiftDown.MaxHeap<Integer> maxHeap = new heap.shiftDown.MaxHeap<Integer>(100);
        int N = 100; // 堆中元素个数
        int M = 100; // 堆中元素取值范围[0, M)
        for( int i = 0 ; i < N ; i ++ )
            maxHeap.insert( new Integer((int)(Math.random() * M)) );

        Integer[] arr = new Integer[N];
        // 将maxheap中的数据逐渐使用extractMax取出来
        // 取出来的顺序应该是按照从大到小的顺序取出来的
        for( int i = 0 ; i < N ; i ++ ){
            arr[i] = maxHeap.extractMax();
            System.out.print(arr[i] + " ");
        }
        System.out.println();

        // 确保arr数组是从大到小排列的
        for( int i = 1 ; i < N ; i ++ )
            assert arr[i-1] >= arr[i];

    }
}
